package com.farecrawler.app.net;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;

import com.farecrawler.app.airlines.TigerUtils;
import com.farecrawler.app.constants.Constants;
import com.farecrawler.app.exception.FareCrawlException;
import com.farecrawler.app.model.FlightInfo;
import com.farecrawler.app.model.FlightRequest;
import com.farecrawler.app.utils.DateUtils;
import com.farecrawler.app.utils.StringUtils;

@Component("tigerConnection")
public class TigerConnection implements IFareCrawlerConnection {

	private Logger log = Logger.getLogger(TigerConnection.class);
	private Calendar departureDate;
	private Calendar arrivalDate;
	private Document document;
	private List<FlightInfo> departureList;

	/**
	 * Facade method of invoking Tiger Airways and extracting response.
	 */
	public void connect(FlightRequest flightRequest) throws FareCrawlException {
		
		log.info("Bootstrap.");
		this.departureDate = flightRequest.getDepartureDate();
		this.arrivalDate = flightRequest.getArrivalDate();

		log.info("Setting up request parameters.");
		Map<String, String> requestParams = setupRequestParameters(flightRequest);
		
		log.info("Submitting HTTP request for " + Constants.TIGER + ".");
		submitRequest(requestParams);

		log.info("Processing HTTP response.");
		processResponse();
	}
	
	/**
	 * Setting up Http requests parameters using the values from FlightRequest object.
	 * 
	 * @param flightRequest
	 * @return
	 */
	private Map<String, String> setupRequestParameters(FlightRequest flightRequest) {
		
		String departYearMonth = DateUtils.convertAsYearMonth(flightRequest.getDepartureMonth(), flightRequest.getDepartureYear());
		String departDayString = DateUtils.convertAsDay(flightRequest.getDepartureDay());
		
		String arrivalYearMonth = DateUtils.convertAsYearMonth(flightRequest.getArrivalMonth(), flightRequest.getArrivalYear());
		String arrivalDayString = DateUtils.convertAsDay(flightRequest.getArrivalDay());
				
		String origin = flightRequest.getOrigin().toUpperCase();  //TODO should have a constants class for countries
		String destination = flightRequest.getDestination().toUpperCase();
		
		return TigerUtils.getRequestParamsMap(departDayString, departYearMonth, arrivalDayString, arrivalYearMonth, origin, destination);
	}

	/**
	 * Load the PostMethod object with the URL, request params and headers
	 * params.
	 * 
	 * @throws FareCrawlException
	 */
	private void submitRequest(Map<String, String> requestParams) throws FareCrawlException {

		Connection connection = Jsoup.connect(TigerUtils.URL);
		connection.data(requestParams);
		
		try {
			document = connection.post();
			
		} catch (IOException e) {
			throw new FareCrawlException(e.getMessage());
		}
	}

	/**
	 * Facade method for extracting flight information.
	 */
	private void processResponse() {
		
		String departurePattern = StringUtils.getRegexForDate(departureDate);
		departureList = extractFlightDetails(
				document.getElementById(TigerUtils.ELEMENT_ID_DEPARTURE),
				departurePattern);
		
//		String arrivalPattern = StringUtils.getRegexForDate(arrivalDate);		
//		returnList = extractFlightDetails(
//				document.getElementById(TigerUtils.ELEMENT_ID_RETURN),
//				arrivalPattern); 
	}
	
	/**
	 * Extract flight details
	 * 
	 * @param element
	 * @param flightDate
	 * @return
	 */
	private List<FlightInfo> extractFlightDetails(Element element, String flightDate) {
		
		Elements tdElements = element.getElementsByTag(TigerUtils.TABLE_ELEMENT);
		List<FlightInfo> flightList = new ArrayList<FlightInfo>();

		for (Element e : tdElements) {
			String dateTest = e.text();

			if (dateTest.matches(flightDate)) {
				FlightInfo info = processFlightInformation(e);
				flightList.add(info);
			}
		}
		return flightList;
	}

	/**
	 * Extract flight information from the given element.
	 * Note that if website were to change, this method will be useless in extracting the
	 * needed information.
	 * 
	 * @param element
	 * @return
	 */
	private FlightInfo processFlightInformation(Element element) {
		
		String[] flightInfoArr = new String[TigerUtils.FLIGHT_TABLE_CELLS_RANGE];
		
		for(int i=0; i<TigerUtils.FLIGHT_TABLE_CELLS_RANGE; i++) {
			String info = element.text();
			flightInfoArr[i] = info;
			element = element.nextElementSibling();
		}
		
		return mapToFlightInfo(flightInfoArr);
	}
	
	
	/**
	 * Map values in array to a FlightInfo object. 
	 * Strictly for use with TigerConnection.processFlightInformation() only!
	 * 
	 * @param arr
	 * @return
	 */
	private FlightInfo mapToFlightInfo(String[] arr) {
		
		FlightInfo info = new FlightInfo();
		info.setFlightDate(arr[0]);
		info.setFlight(arr[1]);
		info.setDepartureTime(arr[2]);
		info.setArrivalTime(arr[3]);
		info.setTicketPrice(arr[5]); //skipped arr[4] because its irrelevant info
		
		return info;
	}

	/*
	 * Getters Setters
	 */
	public Document getDocument() {
		return document;
	}
	public void setDocument(Document document) {
		this.document = document;
	}
	public List<FlightInfo> getDepartureList() {
		return departureList;
	}
	public void setDepartureList(List<FlightInfo> departureList) {
		this.departureList = departureList;
	}
	
}
